//
//  BBCoverFlowView.m
//  BBCoverFlowTest
//
//  Created by ben smith on 2/10/08.
//  Copyright 2008 __MyCompanyName__. All rights reserved.
//

#import "BBCoverFlowView.h"


@implementation BBCoverFlowView

@synthesize delegateLayer;

-(void)awakeFromNib
{
	[self setupLayers];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
	[self layoutSelected];
}

-(NSArray*)observingKeyPaths
{
	return [NSArray arrayWithObjects:@"BBNewmanAngle",@"BBNewmanFactor",@"BBFlowSpacing",@"BBFlowVerticalOffset",@"BBButtonVerticalOffset",nil];
}

-(void)setupLayers
{
	for (NSString * keyPath in [self observingKeyPaths]) {
		[[NSUserDefaults standardUserDefaults] addObserver:self forKeyPath:keyPath options:NSKeyValueObservingOptionNew context:nil];			
	}
	[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(blobDownEvent:) name:@"BBBlobDownEvent" object:nil];
	
	CALayer * root = [CALayer layer];
	//root.backgroundColor = CGColorCreateGenericRGB(0.0, 0.0, 0.0, 1.0);
	//root.borderColor = CGColorCreateGenericRGB(0.0, 1.0, 0.0, 0.5); // yellow
	//root.borderWidth = 1.0;
	//root.opaque = YES;
	root.name = @"rootLayer";
	root.layoutManager = [CAConstraintLayoutManager layoutManager];
	
	[self setLayer:root];
	[self setWantsLayer:YES];
	
	bogusLayer = [[CALayer layer] retain];
	[root addSublayer:bogusLayer];
	containerLayer = [[CALayer layer] retain];
	containerLayer.name = @"container";
	//	containerLayer.frame = CGRectMake(0, 0, 10, 10);
	//containerLayer.backgroundColor = CGColorCreateGenericRGB(0.0, 0.0, 0.0, 1.0);
	//	containerLayer.borderColor = CGColorCreateGenericRGB(1.0, 0.5, 0.0, 0.5); // yellow
	//	containerLayer.borderWidth = 1.0;
	[containerLayer addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMinX relativeTo:@"superlayer" attribute:kCAConstraintMinX offset:0]]; // 10
	[containerLayer addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMaxX relativeTo:@"superlayer" attribute:kCAConstraintMaxX offset:0]]; // -10
	[containerLayer addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMaxY relativeTo:@"superlayer" attribute:kCAConstraintMaxY offset:0]]; // -50
	[containerLayer addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMinY relativeTo:@"superlayer" attribute:kCAConstraintMinY offset:0]]; // 50
	[root addSublayer:containerLayer];
	//containerLayer.layoutManager = [CAConstraintLayoutManager layoutManager];
	
	// the central scrolling layer; this will contain the images
	bodyLayer = [CAScrollLayer layer];
	bodyLayer.scrollMode = kCAScrollHorizontally;
	bodyLayer.autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable;
//	bodyLayer.borderColor = CGColorCreateGenericRGB(0.0, 0.5, 1.0, 0.5); // yellow
//	bodyLayer.borderWidth = 1.0;
	
	[containerLayer addSublayer:bodyLayer];
	
	// create a gradient image to use for our image shadows
	CGRect r;
	r.origin = CGPointZero;
	r.size.height = 180;
	r.size.width = 120;
	size_t bytesPerRow = 4*r.size.width;
	void* bitmapData = malloc(bytesPerRow * r.size.height);
	CGContextRef context = CGBitmapContextCreate(bitmapData, r.size.width,
																							 r.size.height, 8,  bytesPerRow, 
																							 CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB), kCGImageAlphaPremultipliedFirst);
	NSGradient *gradient = [[NSGradient alloc] initWithStartingColor:[NSColor colorWithDeviceWhite:0 alpha:.5] endingColor:[NSColor colorWithDeviceWhite:0 alpha:1.]];
	NSGraphicsContext *nsContext = [NSGraphicsContext graphicsContextWithGraphicsPort:context flipped:YES];
	[NSGraphicsContext saveGraphicsState];
	[NSGraphicsContext setCurrentContext:nsContext];
	[gradient drawInRect:NSMakeRect(0, 0, r.size.width, r.size.height) angle:90];
	[NSGraphicsContext restoreGraphicsState];
	shadowImage = CGBitmapContextCreateImage(context);
	CGContextRelease(context);
	free(bitmapData);
	[gradient release];
	
	
	CALayer *maskLayer = [CALayer layer];
	CALayer *leftGradientLayer = [CALayer layer];
	CALayer *rightGradientLayer = [CALayer layer];
	CALayer *bottomGradientLayer = [CALayer layer];
	
	// left
	r.origin = CGPointZero;
	r.size.width = [self frame].size.width;
	r.size.height = [self frame].size.height;
	bytesPerRow = 4*r.size.width;
	bitmapData = malloc(bytesPerRow * r.size.height);
	context = CGBitmapContextCreate(bitmapData, r.size.width,
																	r.size.height, 8,  bytesPerRow, 
																	CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB), kCGImageAlphaPremultipliedFirst);
	gradient = [[NSGradient alloc] initWithStartingColor:[NSColor colorWithDeviceWhite:0. alpha:1.] endingColor:[NSColor colorWithDeviceWhite:0. alpha:0]];
	nsContext = [NSGraphicsContext graphicsContextWithGraphicsPort:context flipped:YES];
	[NSGraphicsContext saveGraphicsState];
	[NSGraphicsContext setCurrentContext:nsContext];
	[gradient drawInRect:NSMakeRect(0, 0, r.size.width, r.size.height) angle:0];
	[NSGraphicsContext restoreGraphicsState];
	CGImageRef gradientImage = CGBitmapContextCreateImage(context);
	leftGradientLayer.contents = (id)gradientImage;
	CGContextRelease(context);
	CGImageRelease(gradientImage);
	free(bitmapData);
	
	// right
	bitmapData = malloc(bytesPerRow * r.size.height);
	context = CGBitmapContextCreate(bitmapData, r.size.width,
																	r.size.height, 8,  bytesPerRow, 
																	CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB), kCGImageAlphaPremultipliedFirst);
	nsContext = [NSGraphicsContext graphicsContextWithGraphicsPort:context flipped:YES];
	[NSGraphicsContext saveGraphicsState];
	[NSGraphicsContext setCurrentContext:nsContext];
	[gradient drawInRect:NSMakeRect(0, 0, r.size.width, r.size.height) angle:180];
	[NSGraphicsContext restoreGraphicsState];
	gradientImage = CGBitmapContextCreateImage(context);
	rightGradientLayer.contents = (id)gradientImage;
	CGContextRelease(context);
	CGImageRelease(gradientImage);
	free(bitmapData);
	
	// bottom
	r.size.width = [self frame].size.width;
	r.size.height = 32;
	bytesPerRow = 4*r.size.width;
	bitmapData = malloc(bytesPerRow * r.size.height);
	context = CGBitmapContextCreate(bitmapData, r.size.width,
																	r.size.height, 8,  bytesPerRow, 
																	CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB), kCGImageAlphaPremultipliedFirst);
	nsContext = [NSGraphicsContext graphicsContextWithGraphicsPort:context flipped:YES];
	[NSGraphicsContext saveGraphicsState];
	[NSGraphicsContext setCurrentContext:nsContext];
	[gradient drawInRect:NSMakeRect(0, 0, r.size.width, r.size.height) angle:90];
	[NSGraphicsContext restoreGraphicsState];
	gradientImage = CGBitmapContextCreateImage(context);
	bottomGradientLayer.contents = (id)gradientImage;
	CGContextRelease(context);
	CGImageRelease(gradientImage);
	free(bitmapData);
	[gradient release];
	
	// the autoresizing mask allows it to change shape with the parent layer
	maskLayer.autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable;
	maskLayer.layoutManager = [CAConstraintLayoutManager layoutManager];
	[leftGradientLayer addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMinX relativeTo:@"superlayer" attribute:kCAConstraintMinX]];
	[leftGradientLayer addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMinY relativeTo:@"superlayer" attribute:kCAConstraintMinY]];
	[leftGradientLayer addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMaxY relativeTo:@"superlayer" attribute:kCAConstraintMaxY]];
	[leftGradientLayer addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMaxX relativeTo:@"superlayer" attribute:kCAConstraintMaxX scale:.5 offset:-400. / 2]];
	[rightGradientLayer addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMaxX relativeTo:@"superlayer" attribute:kCAConstraintMaxX]];
	[rightGradientLayer addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMinY relativeTo:@"superlayer" attribute:kCAConstraintMinY]];
	[rightGradientLayer addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMaxY relativeTo:@"superlayer" attribute:kCAConstraintMaxY]];
	[rightGradientLayer addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMinX relativeTo:@"superlayer" attribute:kCAConstraintMaxX scale:.5 offset:400. / 2]];
	[bottomGradientLayer addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMaxX relativeTo:@"superlayer" attribute:kCAConstraintMaxX]];
	[bottomGradientLayer addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMinY relativeTo:@"superlayer" attribute:kCAConstraintMinY]];
	[bottomGradientLayer addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMinX relativeTo:@"superlayer" attribute:kCAConstraintMinX]];
	[bottomGradientLayer addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMaxY relativeTo:@"superlayer" attribute:kCAConstraintMinY offset:32]];
	
	bottomGradientLayer.masksToBounds = YES;
	
	[maskLayer addSublayer:rightGradientLayer];
	[maskLayer addSublayer:leftGradientLayer];
	[maskLayer addSublayer:bottomGradientLayer];
	// we make it a sublayer rather than a mask so that the overlapping alpha will work correctly
	// without the use of a compositing filter
	[containerLayer addSublayer:maskLayer];
}

-(void)setupButtons
{
	[super setupButtons];
	
	[containerLayer addSublayer:intoButtonLayer];
	[containerLayer addSublayer:outOfButtonLayer];
	[containerLayer addSublayer:lightBoxButtonLayer];
	[containerLayer addSublayer:quitButtonLayer];
	
	[buttonsInView addObject:lightBoxButtonLayer];
	[buttonsInView addObject:quitButtonLayer];
	[buttonsOutOfView addObject:outOfButtonLayer];
	[buttonsOutOfView addObject:intoButtonLayer];
}


-(void)layoutMess
{
	srandom((unsigned)time(0)); 
	NSRect messRect = NSInsetRect([self bounds], 100, 100);
	int zPos = 0;
	int i = 0;
	[CATransaction begin];
	for (CALayer * layer in [bodyLayer sublayers]) {
		if ( i < selected) {
			layer.zPosition = zPos++;
		}
		if ( i == selected) {
			layer.zPosition = zPos--;
		}
		if ( i > selected) {
			layer.zPosition = zPos--;
		}
		    
		srandom((unsigned)time(0)); 
		int randomX = random();
		randomX = randomX&0xff;
		CGFloat xPos = (((CGFloat)randomX/255.0) * NSWidth(messRect));
    int randomY = random();
		randomY = randomY&0xff;
		CGFloat yPos = (((CGFloat)randomY/255.0) * NSHeight(messRect)) + 100;
		int randomAngle = random();
		randomAngle = randomAngle&0xff;
		CGFloat angle = ((CGFloat)randomAngle/255.0) * 3.14;
		layer.position = CGPointMake(xPos, yPos);
		layer.transform = CATransform3DMakeRotation(angle, 0.0, 0.0, 1.0);	
		layer.sublayerTransform = CATransform3DIdentity;
		CALayer * imgLayer = [[layer sublayers] objectAtIndex:0];
		imgLayer.transform = CATransform3DIdentity;
		[self sublayer:imgLayer opacity:0.0];
		i ++;
	}	
	CGPoint scrollPoint = CGPointMake(0, 0);
	[bodyLayer scrollToPoint:scrollPoint];	
	[CATransaction commit];
}

-(void)sublayer:(CALayer*)layer opacity:(CGFloat)f
{
	for (CALayer * sub in [layer sublayers]) {
		sub.opacity = f;		
	}
}


-(void)layoutSelected
{
	if (selected < 0) selected = 0;
	if (selected > ([[bodyLayer sublayers] count] - 1)) selected = [[bodyLayer sublayers] count] -1;

	CGFloat angle = [[NSUserDefaults standardUserDefaults] floatForKey:@"BBNewmanAngle"];
	CATransform3D leftTransform = CATransform3DMakeRotation(-angle, 0, -1, 0);
	CATransform3D rightTransform = CATransform3DMakeRotation(angle, 0, -1, 0);
	CATransform3D centerTransform = CATransform3DMakeScale(1.7, 1.7, 1.0);
//	[CATransaction begin];
	CGPoint scrollPoint;
	int i;
	i = 0;
	int zPos = 0;
	CGFloat x = 0.0;
	
	BOOL directoryButton = NO;
	
	CGFloat newmanFactor = [[NSUserDefaults standardUserDefaults] floatForKey:@"BBNewmanFactor"];

	CATransform3D sublayerTransform = CATransform3DIdentity; 
	sublayerTransform.m34 = 1. / -newmanFactor;

	CGFloat yOffset = NSMidY([self bounds]) + [[NSUserDefaults standardUserDefaults] floatForKey:@"BBFlowVerticalOffset"];
	CGFloat spacing = [[NSUserDefaults standardUserDefaults] floatForKey:@"BBFlowSpacing"];
	for (CALayer * layer in [bodyLayer sublayers]) {
		[layer removeAnimationForKey:@"zPosition"];
		[layer setSublayerTransform:sublayerTransform];
		CALayer * imgLayer = [[layer sublayers] objectAtIndex:0];
		CGPoint p = layer.position;
		
		p.x = x;
		p.y = yOffset;
		if ( i < selected) {
			imgLayer.transform = leftTransform;
			layer.zPosition = zPos++;
			if (![layer valueForKey:@"isMovie"]) imgLayer.contents = [imgLayer valueForKey:@"mipmap"];
			x += spacing;
		}
		if ( i == selected) {
			CGFloat wide = layer.frame.size.width;
			x += wide;
			p.x = x;
			directoryButton = [[layer valueForKey:@"isDirectory"] boolValue];
			imgLayer.transform = centerTransform;
			scrollPoint = p;
			layer.zPosition = zPos--;
			if (![layer valueForKey:@"isMovie"]) imgLayer.contents = [imgLayer valueForKey:@"highRez"];
			x += wide + spacing;
		}
		if ( i > selected) {
			imgLayer.transform = rightTransform;
			layer.zPosition = zPos--;
			x += spacing;
			if (![layer valueForKey:@"isMovie"]) imgLayer.contents = [imgLayer valueForKey:@"mipmap"];
		}
		layer.position = p;
		i++;
	}
	scrollPoint.x -= containerLayer.bounds.size.width/2.0;
	[bodyLayer scrollToPoint:scrollPoint];	

	CABasicAnimation *position = [CABasicAnimation animationWithKeyPath:@"position"];
  position.delegate = self; //to get the animationDidStop:finished: message
  position.fromValue = [NSNumber numberWithFloat:1.0];
  position.toValue = [NSNumber numberWithFloat:0.0];
  [position setValue:@"bogus" forKey:@"name"];
	[bogusLayer addAnimation:position forKey:@"positionAnimation"];
	
	
	// now deal with the buttons
	if (directoryButton) {
		if ([buttonsOutOfView containsObject:intoButtonLayer]) [buttonsOutOfView removeObject:intoButtonLayer];
		if (![buttonsInView containsObject:intoButtonLayer]) [buttonsInView insertObject:intoButtonLayer atIndex:1];
	} else {
		if ([buttonsInView containsObject:intoButtonLayer]) [buttonsInView removeObject:intoButtonLayer];
		if (![buttonsOutOfView containsObject:intoButtonLayer]) [buttonsOutOfView addObject:intoButtonLayer];
	}
	
	
	if (directoryDepth > 0) {
		if ([buttonsOutOfView containsObject:outOfButtonLayer]) [buttonsOutOfView removeObject:outOfButtonLayer];
		if (![buttonsInView containsObject:outOfButtonLayer]) [buttonsInView insertObject:outOfButtonLayer atIndex:1];
	} else {
		if ([buttonsInView containsObject:outOfButtonLayer]) [buttonsInView removeObject:outOfButtonLayer];
		if (![buttonsOutOfView containsObject:outOfButtonLayer]) [buttonsOutOfView addObject:outOfButtonLayer];
		
	}
	[self handleButtonLayout];
	//[CATransaction commit];
}


-(CALayer*)photoLayer
{	
	CALayer * carrier = [CALayer layer];
	//	carrier.borderColor = CGColorCreateGenericRGB(1.0, 1.0, 0.0, 0.5); // yellow
	//	carrier.borderWidth = 1.0;
	
	CGRect r;
	r.origin = CGPointZero;
	r.size.height = 250;
	r.size.width = 250;
	
	[carrier setFrame:r];
	
	CGPoint p = CGPointMake(100, 350);
	carrier.position = p;	
	[carrier setOpaque:YES];
	
//	carrier.borderColor = CGColorCreateGenericRGB(1.0, 1.0, 0.0, 0.5); // yellow
//	carrier.borderWidth = 1.0;
	
	CALayer * imagelayer = [CALayer layer];
	[imagelayer setFrame:r];
	imagelayer.name = @"image";
	imagelayer.backgroundColor = CGColorCreateGenericRGB(0.2, 0.2, 0.2, 1.0);
//	imagelayer.borderColor = CGColorCreateGenericRGB(1.0, 1.0, 0.0, 0.5); // yellow
	//imagelayer.borderWidth = 1.0;
	imagelayer.zPosition = 0;
	[carrier addSublayer:imagelayer];
	[imagelayer setOpaque:YES];

	
	CALayer *sublayer = [CALayer layer];
	r.origin = CGPointMake(0, -r.size.height);
	[sublayer setFrame:r];
	sublayer.name = @"reflection";
	CATransform3D transform = CATransform3DMakeScale(1,-1,1);
	sublayer.transform = transform;
	sublayer.backgroundColor = CGColorCreateGenericRGB(0.2, 0.2, 0.2, 1.0);
	sublayer.zPosition = 0;
	[sublayer setOpaque:YES];
	
	[imagelayer addSublayer:sublayer];
	
	CALayer *gradientLayer = [CALayer layer];
	//r.origin.y += r.size.height;
	// if the gradient rect is exactly the correct size,
	// antialiasing sometimes gives us a line of bright pixels
	// at the edges
	r.origin.x -= .5;
	r.size.height += 1;
	r.size.width += 1;
	gradientLayer.name = @"gradientLayerForImage";
	[gradientLayer setFrame:r];
	[gradientLayer setContents:(id)shadowImage];
	[gradientLayer setOpaque:NO];
	[imagelayer addSublayer:gradientLayer];
	
	return carrier;
}



-(void)convertToMovieLayer:(CALayer*)aLayer
{		
	// first remove the photolayer
	QTMovie * movie = [QTMovie movieWithFile:aLayer.name error:nil];
	CALayer * oldimagelayer = [[aLayer sublayers] objectAtIndex:0];
	[oldimagelayer removeFromSuperlayer];
	
	NSSize imageSize = [[movie posterImage] size];
	
	CGRect frame = CGRectMake(0, 0, imageSize.width, imageSize.height);
	CGPoint oldLayerPos = aLayer.position;
	
	[aLayer setFrame:frame];
	[aLayer setPosition:oldLayerPos];
	
	// now build up my movie layer
	QTMovieLayer * movielayer = [QTMovieLayer layerWithMovie:movie];
	[movielayer setFrame:frame];
	
	movielayer.name = @"movielayer";
	movielayer.position = CGPointMake(frame.size.width/2.0,frame.size.height/2.0);
  [aLayer addSublayer:movielayer];
	
	QTMovieLayer * sublayer = [QTMovieLayer layerWithMovie:movie];
	frame.origin = CGPointMake(0, -frame.size.height);
	[sublayer setFrame:frame];
	sublayer.name = @"reflection";
	CATransform3D transform = CATransform3DMakeScale(1,-1,1);
	sublayer.transform = transform;
	sublayer.backgroundColor = CGColorCreateGenericRGB(0.2, 0.2, 0.2, 1.0);
	sublayer.zPosition = 0;
	[sublayer setOpaque:YES];
	
	[movielayer addSublayer:sublayer];
	
	CALayer *gradientLayer = [CALayer layer];
	//r.origin.y += r.size.height;
	// if the gradient rect is exactly the correct size,
	// antialiasing sometimes gives us a line of bright pixels
	// at the edges
	frame.origin.x -= .5;
	frame.size.height += 1;
	frame.size.width += 1;
	gradientLayer.name = @"gradientLayerForImage";
	[gradientLayer setFrame:frame];
	[gradientLayer setContents:(id)shadowImage];
	[gradientLayer setOpaque:NO];
	gradientLayer.transform = transform;
	[movielayer addSublayer:gradientLayer];
	
	selected = 0;
	[self layoutSelected];
	
	[[movielayer movie] play];
	[[movielayer movie] setMuted:YES];
	
	[[movielayer movie] setAttribute:[NSNumber numberWithBool:YES] forKey:QTMovieLoopsAttribute];
	
}


-(void)blobDownEvent:(NSNotification*)note
{
	[self mouseDown:[note object]];	
}

-(void)otherMouseDown:(NSEvent*)theEvent
{
	[self mouseDown:theEvent];		
}

-(void)otherMouseUp:(NSEvent*)theEvent
{
	[self mouseUp:theEvent];		
}

-(void)otherMouseDragged:(NSEvent*)theEvent
{
	[self mouseDragged:theEvent];		
}

-(void)mouseDown:(NSEvent*)theEvent
{
	mouseDown = [self convertPoint:[theEvent locationInWindow] toView:self];

	CALayer * hitLayer = [containerLayer hitTest:NSPointToCGPoint(mouseDown)];
	if ([hitLayer superlayer] == containerLayer) {
		// then it is a button
		[self handleButtonPress:hitLayer];
		return;
	}
	didScroll = NO;
	swipeAccumulator = 0;
	scrollAccumulator = 0;
	startIndex = selected;

}

-(void)mouseDragged:(NSEvent*)theEvent
{
	NSPoint thisMouseDown = [self convertPoint:[theEvent locationInWindow] toView:self];
	CGFloat distance = thisMouseDown.x - mouseDown.x;
	mouseDown = thisMouseDown;
	// check the swipe, stuff distance into that one first
	if (fabs(swipeAccumulator) < [[NSUserDefaults standardUserDefaults] floatForKey:@"BBSwipeThreshold"]) {
		swipeAccumulator += distance;
		return;
	}
	
	// here the swipeAccumulator has been overrun, so now we move on to scrolling
	scrollAccumulator += distance;
	
	CGFloat numberOfFlips = floorf(scrollAccumulator / [[NSUserDefaults standardUserDefaults] floatForKey:@"BBFlowFlipDistance"]);
	selected = (startIndex - numberOfFlips);
	didScroll = YES;
	[self layoutSelected];
}

-(void)mouseUp:(NSEvent*)theEvent
{
	if (didScroll) return;
	if (fabs(swipeAccumulator) < [[NSUserDefaults standardUserDefaults] floatForKey:@"BBTapThreshold"]) {
		// just a tap
		[self coverFlowMouseDown:theEvent];		
		return;
	}
	if (fabs(swipeAccumulator) < ([[NSUserDefaults standardUserDefaults] floatForKey:@"BBSwipeThreshold"] + [[NSUserDefaults standardUserDefaults] floatForKey:@"BBFlowFlipDistance"])) {
		// then it is a swipe!
		if (swipeAccumulator < 0) selected ++;
		if (swipeAccumulator > 0) selected --;
		[self layoutSelected];
		return;
	}	
}

-(void)coverFlowMouseDown:(NSEvent*)theEvent
{
	NSPoint mouseDown = [self convertPoint:[theEvent locationInWindow] toView:self];
	
	CALayer * hitLayer = [bodyLayer hitTest:NSPointToCGPoint(mouseDown)];
	
	NSInteger selectionIndex = [[bodyLayer sublayers] indexOfObject:hitLayer];
	
	if ([hitLayer.name isEqualToString:@"image"]) {
		selectionIndex = [[bodyLayer sublayers] indexOfObject:hitLayer.superlayer];		
	} else {
		selectionIndex = [[bodyLayer sublayers] indexOfObject:hitLayer];				
	}
	
	if (selectionIndex == NSNotFound) return;
	selected = selectionIndex;
	[self layoutSelected];		
	// then maybe we hit a button layer?
}



-(void)handleButtonPress:(CALayer*)buttonLayer
{
	if (buttonLayer == intoButtonLayer) {  // go into the directory
		[self openDirectory];
	} 
	if (buttonLayer == outOfButtonLayer) { 
		[self closeDirectory];
	} 
	if (buttonLayer == lightBoxButtonLayer) { 
		[self switchViews];
	} 
	if (buttonLayer == quitButtonLayer) {
		[[NSNotificationCenter defaultCenter] postNotificationName:@"BBQuitAndLaunch" object:self];	
	}
}

-(void)switchViews
{
	self.delegateLayer = bodyLayer;
	
	[CATransaction begin]; 
	
	CABasicAnimation *scale = [CABasicAnimation animationWithKeyPath:@"sublayerTransform"];
  scale.delegate = self; //to get the animationDidStop:finished: message
  scale.fromValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(1.0, 1.0, 1.0)];
  scale.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(0.2, 0.2, 1.0)];
	[scale setValue:@"switch" forKey:@"name"];
  [bodyLayer addAnimation:scale forKey:@"sublayerTransformAnimation"];
	[buttonsOutOfView addObjectsFromArray:buttonsInView];
	[buttonsInView removeAllObjects];
	[self handleButtonLayout];
	for (CALayer * l in [bodyLayer sublayers]) {
		l.opacity = 0;
	}
	[CATransaction commit];
	
}

-(void)closeDirectory
{
	self.delegateLayer = bodyLayer;
	
	[CATransaction begin]; 
	
	CABasicAnimation *scale = [CABasicAnimation animationWithKeyPath:@"sublayerTransform"];
  scale.delegate = self; //to get the animationDidStop:finished: message
  scale.fromValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(1.0, 1.0, 1.0)];
  scale.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(0.2, 0.2, 1.0)];
  [bodyLayer addAnimation:scale forKey:@"sublayerTransformAnimation"];
	
	for (CALayer * l in [bodyLayer sublayers]) {
		l.opacity = 0;
	}
	[CATransaction commit];
}


-(void)openDirectory
{
	CALayer * titleCard = [[bodyLayer sublayers] objectAtIndex:selected];
	NSMutableArray * trashBag = [NSMutableArray array];
	for (CALayer * l in [bodyLayer sublayers]) {
		if (l != titleCard ) [trashBag addObject:l];
	}
	[trashBag makeObjectsPerformSelector:@selector(removeFromSuperlayer)];
	
	self.delegateLayer = titleCard;
	
	[CATransaction begin]; 

	CABasicAnimation *opacity = [CABasicAnimation animationWithKeyPath:@"opacity"];
  opacity.delegate = self; //to get the animationDidStop:finished: message
  opacity.fromValue = [NSNumber numberWithFloat:1.0];
  opacity.toValue = [NSNumber numberWithFloat:0.0];
  [titleCard addAnimation:opacity forKey:@"opacityAnimation"];

	bodyLayer.transform = CATransform3DMakeScale(1.5, 1.5, 1.0);
	bodyLayer.opacity = 0;
	titleCard.opacity = 0;
	[CATransaction commit];
}

-(void)resetLOD
{
	int i;
	for (CALayer * layer in [bodyLayer sublayers]) {
		CALayer * imgLayer = [[layer sublayers] objectAtIndex:0];
		[imgLayer removeAllAnimations];
		if ( i < selected) {
			imgLayer.contents = [imgLayer valueForKey:@"mipmap"];
			continue;
		}
		if ( i > selected) {
			imgLayer.contents = [imgLayer valueForKey:@"mipmap"];
			continue;
		}
		imgLayer.contents = [imgLayer valueForKey:@"higRez"];
		layer.sublayerTransform = CATransform3DIdentity;
		CGRect r = imgLayer.frame;
		r.size.height *= 1.7;
		r.size.width *= 1.7;
		imgLayer.frame = r;
		i++;
	}
}


-(void)setContents:(NSBitmapImageRep*)bitRep mipMap:(NSBitmapImageRep*)mipRep toLayer:(NSString*)name
{
	CALayer * thisLayer = [self layerWithName:name];
	if (thisLayer == nil) return;
	
	NSSize imageSize = [mipRep size];

	CALayer * imagelayer = [[thisLayer sublayers] objectAtIndex:0];
	CALayer * reflectionlayer = [[imagelayer sublayers] objectAtIndex:0];
	CGRect f = [imagelayer frame];
	f.size.width = imageSize.width;
	f.size.height = imageSize.height;
	
	CGPoint p = thisLayer.position;
	[thisLayer setFrame:f];
	thisLayer.position = p;
	
	[imagelayer setFrame:f];
	imagelayer.position = CGPointMake(f.size.width/2.0,f.size.height/2.0);
	
	
	f.origin = CGPointMake(0, -f.size.height);
	[reflectionlayer setFrame:f];
	
	imagelayer.contents = (id)[mipRep CGImage];
	reflectionlayer.contents = (id)[mipRep CGImage];
	
	[imagelayer setValue:bitRep forKey:@"bitRep"];
	[imagelayer setValue:mipRep forKey:@"mipRep"];
	
	[imagelayer setValue:(id)[bitRep CGImage] forKey:@"highRez"];
	[reflectionlayer setValue:(id)[bitRep CGImage] forKey:@"highRez"];
	
	[imagelayer setValue:(id)[mipRep CGImage] forKey:@"mipmap"];
	[reflectionlayer setValue:(id)[mipRep CGImage] forKey:@"mipmap"];
	
	CALayer * gradient = [[imagelayer sublayers] objectAtIndex:1];
	CGRect gFrame = [gradient frame];
	CGFloat xScale = imageSize.width/gFrame.size.width;
	CGFloat yScale = imageSize.height/gFrame.size.height;
	CATransform3D scale = CATransform3DMakeScale(xScale, -yScale, 1.0);
	gradient.transform = scale;
	f.origin.x -= .5;
	f.size.height += 1;
	f.size.width += 1;
	[gradient setFrame:f];
	if (selected == -1) {
		selected = 0;
		[self layoutSelected];
	}
	
}

- (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag
{
	if ([[theAnimation valueForKey:@"name"] isEqualToString:@"bogus"]) {
	//	[self resetLOD];
		return;
	}

	if ([[theAnimation valueForKey:@"name"] isEqualToString:@"switch"]) {
		NSString * path = [[[bodyLayer sublayers] lastObject] name];
		path = [path stringByDeletingLastPathComponent];
		[[NSNotificationCenter defaultCenter] postNotificationName:@"BBShuffleViewsNotification" object:path];	
		return;
	}
	
	if (self.delegateLayer == bodyLayer) {
		// then something
		[self handleCloseDirectoryFinish];
		return;
	}
	[self handleOpenDirectoryFinish];
}

-(void)handleCloseDirectoryFinish 
{
	NSString * path = [[[bodyLayer sublayers] lastObject] name];
	path = [[path stringByDeletingLastPathComponent] stringByDeletingLastPathComponent];
		
	NSArray * trashBag = [NSArray arrayWithArray:[bodyLayer sublayers]];
	[trashBag makeObjectsPerformSelector:@selector(removeFromSuperlayer)];
	
	self.delegateLayer = nil;
	directoryDepth --;
	
	NSArray * fileNames = [[NSFileManager defaultManager] directoryContentsAtPath:path];
	NSMutableArray * fullPaths = [NSMutableArray array];
	for (NSString * name in fileNames) {
		if ([name hasPrefix:@"."]) continue;
		[fullPaths addObject:[path stringByAppendingPathComponent:name]];
	}
	if (directoryDepth > 0)
		[fullPaths removeObjectAtIndex:0]; // pop off the title card
	selected = -1;
	[self addImageFiles:fullPaths];

}

-(void)addImageFiles:(NSArray*)fileNames;
{
	selected = -1;
	// testing:
	[super addImageFiles:fileNames];
}

-(void)handleOpenDirectoryFinish 
{
	[CATransaction begin]; 
	[bodyLayer removeAllAnimations];
	bodyLayer.transform = CATransform3DMakeScale(0.1, 0.1, 1.0);
	[CATransaction commit];
	
	bodyLayer.transform = CATransform3DMakeScale(1.0,1.0,1.0);
	bodyLayer.opacity = 1.0;
	
	[self.delegateLayer removeFromSuperlayer];
	
	directoryDepth ++;
	
	NSArray * fileNames = [[NSFileManager defaultManager] directoryContentsAtPath:self.delegateLayer.name];
	NSMutableArray * fullPaths = [NSMutableArray array];
	for (NSString * name in fileNames) {
		if ([name hasPrefix:@"."]) continue;
		[fullPaths addObject:[self.delegateLayer.name stringByAppendingPathComponent:name]];
	}
	[fullPaths removeObjectAtIndex:0]; // pop off the title card
	selected = -1;
	[self addImageFiles:fullPaths];
	self.delegateLayer = nil;
}

- (void) dealloc
{
	[[NSNotificationCenter defaultCenter] removeObserver:self];
	for (NSString * keyPath in [self observingKeyPaths]) {
		[[NSUserDefaults standardUserDefaults] removeObserver:self forKeyPath:keyPath];
	}
	
	[super dealloc];
}



@end

